mod core;
use crate::core::vec3::*;
use crate::core::ray::*;
use crate::core::hittable::*;
use crate::core::rtweekend::*;
use crate::core::sphere::*;
use crate::core::camera::*;
use crate::core::material::*;
use std::rc::Rc;

fn ray_color(r: &Ray, world: &dyn Hittable, depth: i32) -> Color3{
    if depth <= 0{
        return Color3::new();
    }
    match world.hit(&r, 0.001_f64, INFINITY){
        Some(hit_rec) => {
            // return 0.5_f64 * &(hit_rec.normal.unwrap() + &Color3::from(1_f64, 1_f64, 1_f64));
            // 漫反射
            // let target = &hit_rec.p + &hit_rec.normal.unwrap() + &random_in_unit_sphere();
            // 增加兰伯特反射
            // let target = &hit_rec.p + &hit_rec.normal.unwrap() + &random_unit_vector();
            // 优化
            // let target = &hit_rec.p + &random_in_hemisphere(&hit_rec.normal.unwrap());
            // return 0.5_f64 * &ray_color(&Ray::from(&hit_rec.p, &(&target - &hit_rec.p)), world, depth-1);

            // 增加材质
            // if let Some((attenuation, scattered)) = hit_rec.mat_ptr.scatter(r, &hit_rec){
            //     return &attenuation*&ray_color(&scattered, world, depth-1);
            // }
            match hit_rec.mat_ptr.scatter(r, &hit_rec){
                Some((attenuation, scattered)) =>{
                    return &attenuation*&ray_color(&scattered, world, depth-1);
                },
                None =>{
                    return Color3::new();
                }
            }
        },
        None =>{
            let unit_direction = unit_vector(&r.direction());
            let t = 0.5_f64 * (unit_direction.get_y() + 1_f64);
            return (1_f64 - t) * &Color3::from(1_f64, 1_f64, 1_f64) + t * &Color3::from(0.5_f64, 0.7_f64, 1_f64);
        }
    }

    // let mut t = hit_sphere(&Point3::from(0_f64, 0_f64, -1_f64), 0.5_f64, r);
    // if t > 0_f64 {
    //     let N = unit_vector(&(r.at(t) - Vec3New::from(0_f64, 0_f64, -1_f64)));
    //     return 0.5_f64 * &Color3::from(N.get_x()+1_f64, N.get_y()+1_f64, N.get_z()+1_f64);
    // }
    // let unit_direction = unit_vector(&r.direction());
    // t = 0.5_f64 * (unit_direction.get_y() + 1_f64);
    // (1_f64 - t) * &Color3::from(1_f64, 1_f64, 1_f64) + t * &Color3::from(0.5_f64, 0.7_f64, 1_f64)
}

fn hit_sphere(center: &Point3, radius: f64, r: &Ray) -> f64 {
    let oc = r.origin() - center;
    // let a = dot(&r.direction(), &r.direction());
    // let b = 2_f64 * dot(&oc, &r.direction());
    // let c = dot(&oc, &oc) - radius*radius;
    // let discriminant = b*b - 4_f64*a*c;

    let a = r.direction().length_squared();
    let half_b = dot(&oc, &r.direction());
    let c = oc.length_squared() - radius*radius;
    let discriminant = half_b * half_b - a * c;
    if discriminant < 0_f64{
        return -1.0_f64;
    }else{
        // return (-b - discriminant.sqrt()) / (2.0_f64 * a);
        return -half_b - discriminant.sqrt();
    }
}

pub fn random_scence() -> HittbaleList{
    let mut world:HittbaleList = HittbaleList::new();
    let ground_material = Rc::new(Lambertian::from(&Color3::from(0.5, 0.5, 0.5)));
    world.add(Box::new(Sphere::from(&Point3::from(0.0, -1000.0, 0.0), 1000.0, ground_material)));

    for a in -11..11{
        for b in -11..11{
            let choose_mat = random_f64();
            let center = Point3::from((a as f64)+0.9*random_f64(), 0.2, (b as f64)+0.9*random_f64());
            if (center - Point3::from(4.0, 0.2, 0.0)).length() > 0.9{
                let sphere_material: Rc<dyn Material>;
                if choose_mat < 0.8{
                    // diffuse
                    let albedo = &Color3::random_point()*&Color3::random_point();
                    sphere_material = Rc::new(Lambertian::from(&albedo));
                    world.add(Box::new(Sphere::from(&center, 0.2, sphere_material)));
                }else if choose_mat < 0.95 {
                    // metal
                    let albedo = Color3::random_point_minmax(0.5, 1.0);
                    let fuzz = random_range_f64(0.0, 0.5);
                    sphere_material = Rc::new(Metal::from(&albedo, fuzz));
                    world.add(Box::new(Sphere::from(&center, 0.2, sphere_material)));
                }else{
                    // glass
                    sphere_material = Rc::new(Dielectric::from(1.5));
                    world.add(Box::new(Sphere::from(&center, 0.2, sphere_material)));
                }
            }
        }
    }
    let material1 = Rc::new(Dielectric::from(1.5));
    world.add(Box::new(Sphere::from(&Point3::from(0.0, 1.0, 0.0), 1.0, material1)));

    let material2 = Rc::new(Lambertian::from(&Color3::from(0.4, 0.2, 0.1)));
    world.add(Box::new(Sphere::from(&Point3::from(-4.0, 1.0, 0.0), 1.0, material2)));

    let material3 = Rc::new(Metal::from(&Color3::from(0.7, 0.6, 0.5), 0.0));
    world.add(Box::new(Sphere::from(&Point3::from(4.0, 1.0, 0.0), 1.0, material3)));

    world
}


fn main() {
    // Image
    let aspect_ratio = (3.0 / 2.0) as f64;
    let image_width = 1200;
    let image_height = ((image_width as f64) / aspect_ratio) as u32;
    let samples_per_pixel = 500_f64;
    let max_depth = 50_i32;

    // world 
    // let R= (std::f64::consts::PI/4.0).cos();

    // let mut world: HittbaleList = HittbaleList::new();
    // // world.add(Box::new(Sphere::from(&Point3::from(0_f64, 0_f64,-1_f64), 0.5_f64)));
    // // world.add(Box::new(Sphere::from(&Point3::from(0_f64, -100.5_f64,-1_f64), 100_f64)));

    // let material_ground = Rc::new(Lambertian::from(&Color3::from(0.8_f64, 0.8_f64, 0_f64)));
    // // let material_center = Rc::new(Lambertian::from(&Color3::from(0.7_f64, 0.3_f64, 0.3_f64)));
    // let material_center = Rc::new(Lambertian::from(&Color3::from(0.1, 0.2, 0.5)));
    // // let material_left = Rc::new(Metal::from(&Color3::from(0.8_f64, 0.8_f64, 0.8_f64), 0.3_f64));
    // let material_left = Rc::new(Dielectric::from(1.5_f64));
    // let material_right = Rc::new(Metal::from(&Color3::from(0.8_f64, 0.6_f64, 0.2_f64), 0.0_f64));

    // world.add(Box::new(Sphere::from(&Point3::from(0_f64, -100.5_f64,-1_f64), 100_f64, material_ground)));
    // world.add(Box::new(Sphere::from(&Point3::from(0_f64, 0_f64, -1_f64), 0.5_f64, material_center)));
    // world.add(Box::new(Sphere::from(&Point3::from(-1_f64, 0_f64,-1_f64), 0.5_f64, material_left.clone())));
    // world.add(Box::new(Sphere::from(&Point3::from(-1_f64, 0_f64,-1_f64), 0.4_f64, material_left.clone())));
    // world.add(Box::new(Sphere::from(&Point3::from(1_f64, 0_f64,-1_f64), 0.5_f64, material_right)));

    // let material_left = Rc::new(Lambertian::from(&Color3::from(0.0, 0.0, 1.0)));
    // let material_right = Rc::new(Lambertian::from(&Color3::from(1.0, 0.0, 0.0)));

    // world.add(Box::new(Sphere::from(&Point3::from(-R, 0.0, -1.0), R, material_left)));
    // world.add(Box::new(Sphere::from(&Point3::from(R, 0.0, -1.0), R, material_right)));

    let world = random_scence();

    // Camera
    // let viewport_height = 2.0;
    // let viewport_width = aspect_ratio * viewport_height;
    // let focal_length = 1.0;
    let lookfrom = Point3::from(13.0, 2.0, 3.0);
    let lookat = Point3::from(0.0, 0.0, 0.0);
    let vup = Vec3New::from(0.0, 1.0, 0.0);
    // let dist_to_fous = (&lookfrom-&lookat).length();
    let dist_to_fous = 10.0;
    let aperture = 0.1;

    let cam = Camera::new(lookfrom, lookat, vup, 20.0, aspect_ratio, aperture, dist_to_fous);

    // let origin = Point3::new();
    // let horizontal = Vec3New::from(viewport_width, 0.0, 0.0);
    // let vertical = Vec3New::from(0.0, viewport_height, 0.0);

    // // 设置光照方向的点位左下角，光从origin->lower_left_corner
    // let lower_left_corner = &origin - &(&horizontal/2_f64) - &(&vertical/2_f64) - &Vec3New::from(0.0, 0.0, focal_length);

    // Render
    println!("{}",format!("P3\n {0} {1} \n255\n", image_width,image_height));
    let mut stdout = std::io::stdout();

    for j in (0..(image_height)).rev(){
        eprintln!("\rScanlines remaining: {}", j);
        for i in 0..image_width{
            // let u = (i as f64)/(image_width-1) as f64;
            // let v = (j as f64)/(image_height-1) as f64;
            // let r = Ray::from(&origin, &(&lower_left_corner + &(u*&horizontal) + &(v*&vertical) - &origin));

            // let pixel_color = ray_color(&r, &world);
            // core::color::write_color(&mut stdout, pixel_color).unwrap();

            let mut pixel_color = Color3::new();
            for _s in 0..(samples_per_pixel as i32){
                let u = ((i as f64) + random_f64()) / (image_width-1) as f64;
                let v = ((j as f64) + random_f64()) / (image_height-1) as f64;
                let r = cam.get_ray(u, v);
                pixel_color += ray_color(&r, &world, max_depth);
            }
            core::color::write_color(&mut stdout, pixel_color, &samples_per_pixel).unwrap();
        }
    }

    // 下面的是准备工作
    // // Image
    // let image_width = 256;
    // let image_height = 256;

    // // Render
    // // println!("P3\n" + image_width + " " + image_height + "\n255\n");
    // println!("{}",format!("P3\n {0} {1} \n255\n", image_width,image_height));

    // let mut stdout = std::io::stdout();
    // for j in (0..(image_height)).rev(){
    //     eprintln!("\rScanlines remaining: {}", j);
    //     for i in 0..image_width{
    //         // let r = (i as f32)/(image_width-1) as f32;
    //         // let g = (j as f32)/(image_height-1) as f32;
    //         // let b = 0.25 as f32;

    //         // let ir = (255.99 * r) as u8;
    //         // let ig = (255.99 * g) as u8;
    //         // let ib = (255.99 * b) as u8;
    //         // // println!("{}",format!("{0} {1} {2}",ir,ig,ib));
    //         // println!("{} {} {}", ir,ig,ib);

    //         // let mut file = File::create("output.ppm").unwrap();
    //         // let pixel_color = core::vec3::Color3::from((i as f64) / ((image_width-1) as f64), (j as f64)/((image_height - 1) as f64), 0.25_f64);
    //         // core::color::write_color(&mut file, pixel_color).unwrap();

    //         let pixel_color = core::vec3::Color3::from((i as f64) / ((image_width-1) as f64), (j as f64)/((image_height - 1) as f64), 0.25_f64);
    //         core::color::write_color(&mut stdout, pixel_color).unwrap();
    //     }
    // }
    println!("\nDone.\n");

}
